﻿using System;
using System.Fakes;
using System.Windows.Threading.Fakes;
using Microsoft.QualityTools.Testing.Fakes;
using ShimsDemo.SystemUnderTest;
using ShimsDemo.SystemUnderTest.Fakes;
using Xunit;

public class CustomerViewModelTests
{
	[Fact]
	public void SaveShouldSetTheCorrectLastUpdatedDate_WithoutShim()
	{
		// Arrange
		var savedCustomer = default(Customer); // null
		var repository = new StubICustomerRepository
			  {
				  SaveOrUpdateCustomer = customer => savedCustomer = customer
			  };
		var actualCustomer = new Customer
			  {
				  Id = 1,
				  Name = "Sample Customer",
				  LastUpdated = DateTime.MinValue
			  };
		var viewModel = new CustomerViewModel(actualCustomer, repository);

		// Act
		var now = DateTime.Now;
		viewModel.Save();

		// Assert
		Assert.NotNull(savedCustomer);

		// We will use a 10ms "window" to confirm that the date is "close enough"
		// to what we expect. Not ideal, but it should work... most of the time.
		var delta = Math.Abs((savedCustomer.LastUpdated - now).TotalMilliseconds);
		const int accuracy = 10; // milliseconds
		Assert.True(delta <= accuracy, "LastUpdated was not appx equal to expected");
	}

	[Fact]
	public void SaveShouldSetTheCorrectLastUpdatedDate_WithShim()
	{
		using (ShimsContext.Create())
		{
			// Arrange
			var savedCustomer = default(Customer); // null
			var repository = new StubICustomerRepository
				  {
					  SaveOrUpdateCustomer = customer => savedCustomer = customer
				  };

			// Make DateTime.Now always return midnight Jan 1, 2012
			ShimDateTime.NowGet = () => new DateTime(2012, 1, 1);

			var actualCustomer = new Customer
				  {
					  Id = 1,
					  Name = "Sample Customer",
					  LastUpdated = DateTime.MinValue
				  };
			var viewModel = new CustomerViewModel(actualCustomer, repository);

			// Act
			viewModel.Save();

			// Assert
			Assert.NotNull(savedCustomer);
			Assert.Equal(new DateTime(2012, 1, 1), savedCustomer.LastUpdated);
		}
	}

	[Fact]
	public void TimerStartedWith1secInterval()
	{
		using (ShimsContext.Create())
		{
			// Arrange
			var customer = new Customer();
			var repository = new StubICustomerRepository();
			var sut = new CustomerViewModel(customer, repository);

			var span = default(TimeSpan);
			var startCalled = false;
			ShimDispatcherTimer.AllInstances.IntervalSetTimeSpan = (@this, ts) => span = ts;
			ShimDispatcherTimer.AllInstances.Start = (@this) => startCalled = true;

			// Act
			sut.StartTimer();

			// Assert
			Assert.Equal(TimeSpan.FromSeconds(1), span);
			Assert.True(startCalled);
		}
	}

	[Fact]
	public void RefreshTimerCallsRefreshAfter30Ticks()
	{
		using (ShimsContext.Create())
		{
			// Arrange
			var customer = new Customer();
			var repository = new StubICustomerRepository();
			var sut = new CustomerViewModel(customer, repository);

			var refreshDataWasCalled = false;
			new ShimCustomerViewModel(sut)
			{
				RefreshData = () => refreshDataWasCalled = true,
			};

			// Setup our override of the Dispatch timer
			// We will store the tick handler so we can "pump" it
			// and noop override Interval and Start just to be safe
			var tick = default(EventHandler);
			ShimDispatcherTimer.AllInstances.TickAddEventHandler = (@this, h) => tick = h;
			ShimDispatcherTimer.AllInstances.IntervalSetTimeSpan = (@this, ts) => { };
			ShimDispatcherTimer.AllInstances.Start = (@this) => { };

			// Act
			sut.StartTimer();

			// Assert
			refreshDataWasCalled = false; // Clear out the any calls that happened before the tick()
			for (var i = 0; i < 29; i++) tick(this, null); // Tick 29 times
			Assert.False(refreshDataWasCalled);
			tick(this, null); // Tick one more time
			Assert.True(refreshDataWasCalled);
		}
	}

}
